package controllers
import (
	"net/http"
	"github.com/bitly/go-simplejson"
	"io/ioutil"
	"ServerGo/common"
	"os/exec"
	"os"
	"reflect"
	"encoding/json"
	"strings"
)

// @router /composerMerge.php [Post]
// @binding controllers.EmptyParams
func ComposerMergeResponse(params EmptyParams, res http.ResponseWriter, req *http.Request) string {
	values := req.MultipartForm.Value
	newAutoload, err := simplejson.NewJson([]byte(values["autoload_file"][0]))
	if err != nil {
		res.WriteHeader(501)
		var errInfo string = "new autoload parse error"
		common.Logger(common.LOG_WARNING, errInfo)
		return common.Object2JsonStr(ResponseMsg{5, errInfo, nil})
	}

	newInstalled, err := simplejson.NewJson([]byte(values["installed_file"][0]))
	if err != nil {
		res.WriteHeader(502)
		var errInfo string = "new installed parse error"
		common.Logger(common.LOG_WARNING, errInfo)
		return common.Object2JsonStr(ResponseMsg{5, errInfo, nil})
	}

	bytes, err := ioutil.ReadFile(values["autoload"][0])
	if os.IsNotExist(err) {
		bytes = []byte(`{}`)
	}

	oldAutoload, err := simplejson.NewJson(bytes)
	if err != nil {
		res.WriteHeader(503)
		var errInfo string = "old autoload parse error"
		common.Logger(common.LOG_WARNING, errInfo)
		return common.Object2JsonStr(ResponseMsg{5, errInfo, nil})
	}

	bytes, err = ioutil.ReadFile(values["installed"][0])
	if os.IsNotExist(err) {
		bytes = []byte(`[]`)
	}

	oldInstalled, err := simplejson.NewJson(bytes)
	if err != nil {
		res.WriteHeader(504)
		var errInfo string = "old installed parse error"
		common.Logger(common.LOG_WARNING, errInfo)
		return common.Object2JsonStr(ResponseMsg{5, errInfo, nil})
	}

	autoload := mergeAutoload(oldAutoload, newAutoload, []string{})
	installed := mergeInstalled(oldInstalled, newInstalled)

	bytes, err = autoload.MarshalJSON()
	if err != nil {
		res.WriteHeader(505)
		var errInfo string = "autoload marshal json faild"
		common.Logger(common.LOG_WARNING, errInfo)
		return common.Object2JsonStr(ResponseMsg{6, errInfo, nil})
	}

	err = ioutil.WriteFile(values["autoload"][0], bytes, 0777)
	if err != nil {
		res.WriteHeader(506)
		var errInfo string = "write autoload file faild"
		common.Logger(common.LOG_WARNING, errInfo)
		return common.Object2JsonStr(ResponseMsg{7, errInfo, nil})
	}

	bytes, err = installed.MarshalJSON()
	if err != nil {
		res.WriteHeader(507)
		var errInfo string = "installed marshal json faild"
		common.Logger(common.LOG_WARNING, errInfo)
		return common.Object2JsonStr(ResponseMsg{6, errInfo, nil})
	}

	err = ioutil.WriteFile(values["installed"][0], bytes, 0777)
	if err != nil {
		res.WriteHeader(508)
		var errInfo string = "write installed file faild"
		common.Logger(common.LOG_WARNING, errInfo)
		return common.Object2JsonStr(ResponseMsg{7, errInfo, nil})
	}

	cmd := exec.Command("/bin/sh", "-c", values["command"][0])
	output, err := cmd.CombinedOutput()
	if err != nil {
		res.WriteHeader(509)
		var errInfo string = "git pull error: "+string(output)
		common.Logger(common.LOG_WARNING, errInfo)
		return common.Object2JsonStr(ResponseMsg{3, errInfo, nil})
	}

	if values["type"][0] == "1" {
		errInfo := ""
		_, err := os.Stat(RECORD_PATH)
		if os.IsNotExist(err) {
			res.WriteHeader(510)
			errInfo = "the record path is not exists"
			common.Logger(common.LOG_WARNING, errInfo)
			return common.Object2JsonStr(ResponseMsg{4, "the record path is not exists", nil})
		}else if copyComposerFile2Tmp(values["autoload_file"][0], values["installed_file"][0]) {
			common.Logger(common.LOG_INFO, "success")
			return common.Object2JsonStr(ResponseMsg{0, "success", nil})
		}else {
			res.WriteHeader(511)
			var errInfo string = "write to file faild"
			common.Logger(common.LOG_WARNING, errInfo)
			return common.Object2JsonStr(ResponseMsg{2, "write to file faild", nil})
		}
	}

	return common.Object2JsonStr(ResponseMsg{0, "success", nil})
}

func mergeAutoload(oldJ *simplejson.Json, newJ *simplejson.Json, path []string) *simplejson.Json {
	newMap, _ := newJ.Map()
	for k, v := range newMap {
		path = append(path, k)
		obj := oldJ.GetPath(path...).Interface()
		if obj == nil {
			oldJ.SetPath(path, v)
		}else {
			_, m := obj.([]interface{})
			_, n := v.([]interface{})
			if m && n {
				oldJ = mergeAutoload(oldJ, newJ, path)
			}else {
				oldJ.SetPath(path, v)
			}
		}
		path = make([]string, 0)
	}

	return oldJ
}

func mergeInstalled(oldJ *simplejson.Json, newJ *simplejson.Json) *simplejson.Json {
	oldMap, _ := oldJ.Array()
	oldKeys := getKeys(oldMap)
	newMap, _ := newJ.Array()
	for _, v := range newMap {
		name := ""
		rname := reflect.ValueOf(v).MapIndex(reflect.ValueOf("name"))
		if rname.IsValid() {
			name = rname.Interface().(string)
		}else {
			name = ""
		}
		index := findIndex(name, oldKeys)
		if index == -1 {
			oldMap = append(oldMap, v)
		}else {
			oldMap[index] = v
		}

	}
	bytes, _ := json.Marshal(oldMap)
	tmp, _ := simplejson.NewJson(bytes)
	return tmp
}



func copyComposerFile2Tmp(newAutoloaded string, newInstalled string) bool {
	recordInfo, _ := ioutil.ReadFile(RECORD_PATH)
	recordInfos := strings.Split(string(recordInfo), "|")
	dirpath := TMP_PATH_PREFIX+"/"+recordInfos[1]+"/"+recordInfos[2]
	_, err := os.Stat(dirpath)
	if os.IsNotExist(err) {
		os.MkdirAll(dirpath, 0777)
	}

	err = ioutil.WriteFile(dirpath+"/composer.json", []byte(newAutoloaded), 0777)
	if err != nil {
		return false
	}

	err = ioutil.WriteFile(dirpath+"/installed.json", []byte(newInstalled), 0777)
	if err != nil {
		return false
	}

	return true
}